#include "MidiIOGlue.h"
#include "GlueTypes.h"

#include "luna.h"

#include <assert.h>

#include "Bridger.h"

class BridgeMIDI_event_t;
class BridgeMIDI_eventlist;
class Bridgemidi_Input;
class Bridgemidi_Output;

mkchelpers(MIDI_event_t);

mkchelpers(MIDI_eventlist);
mkchelpers(midi_Input);
mkchelpers(midi_Output);

class BridgeMIDI_event_t : public Bridger
{
public:

	BridgeMIDI_event_t(lua_State *L) : Bridger(L)	{}
// luna wrapper interface:
public:
	static const char className[];
    static const Luna<BridgeMIDI_event_t>::RegType Register[];
};
#define bmetrentry(NAME)	\
{ #NAME , &BridgeMIDI_event_t::f##NAME }

const char BridgeMIDI_event_t::className[] = "ReaperMIDI_event_t";
const Luna<BridgeMIDI_event_t>::RegType BridgeMIDI_event_t::Register[] = {
/*	bmerentry(AddItem),
	bmerentry(EnumItems),
	bmerentry(DeleteItem),
	bmerentry(GetSize),
	bmerentry(Empty),
*/
	{NULL, NULL}
};

class BridgeMIDI_eventlist : public Bridger
{
public:

	BridgeMIDI_eventlist(lua_State *L) : Bridger(L)	{}

// f-unctions

//  virtual void AddItem(MIDI_event_t *evt)=0; // puts the item in the correct place
	// takes ( {self}, {BridgeMIDI_event_t *evt} ) returns void
	int fAddItem(lua_State *L)
	{
		MIDI_eventlist *melp = (MIDI_eventlist *) toReaperObject(L, 1);
		MIDI_event_t *metp = (MIDI_event_t *) toReaperObject(L, 2);
		melp->AddItem(metp);
		return 0;
	}

//  virtual MIDI_event_t *EnumItems(int *bpos)=0; 
//	takes ( {self}, int bpos)
//	returns ( {BridgeMidiEvent} or nil, int nextpos (0=end) )

	int fEnumItems(lua_State *L)
	{
		MIDI_eventlist *melp = (MIDI_eventlist *)   toReaperObject(L, 1);
		int bpos = luaL_checkinteger(L, 2);
		MIDI_event_t *metp = melp->EnumItems(&bpos);

		if(metp)
		{
			pushMIDI_event_t(L, metp);
			lua_pushnumber(L, bpos);
			return 2;
		}

		lua_pushnil(L);
		lua_pushnumber(L, 0);
		return 2;
	}

	//  virtual void DeleteItem(int bpos)=0;
	// takes ( {self} , int bpos ) returns void
	int fDeleteItem(lua_State *L)
	{
		MIDI_eventlist *melp = (MIDI_eventlist *)   toReaperObject(L, 1);
		int bpos = luaL_checkinteger(L, 2);
		melp->DeleteItem(bpos);
		return 0;
	}

//  virtual int GetSize()=0; // size of block in bytes
// takes {self} returns int
	int fGetSize(lua_State *L)
	{
		MIDI_eventlist *melp = (MIDI_eventlist *)   toReaperObject(L, 1);
		int size = melp->GetSize();

		lua_pushnumber(L, size);
		return 1;
	}

//	  virtual void Empty()=0;
// takes {self} returns void
	int fEmpty(lua_State *L)
	{
		MIDI_eventlist *melp = (MIDI_eventlist *)   toReaperObject(L, 1);
		melp->Empty();
		return 0;
	}


// luna wrapper interface:
public:
	static const char className[];
    static const Luna<BridgeMIDI_eventlist>::RegType Register[];
};
#define bmerentry(NAME)	\
{ #NAME , &BridgeMIDI_eventlist::f##NAME }

const char BridgeMIDI_eventlist::className[] = "ReaperMIDI_eventlist";
const Luna<BridgeMIDI_eventlist>::RegType BridgeMIDI_eventlist::Register[] = {
	bmerentry(AddItem),
	bmerentry(EnumItems),
	bmerentry(DeleteItem),
	bmerentry(GetSize),
	bmerentry(Empty),

	{NULL, NULL}
};

/*
	A "ReaperMidiEventList" is an STL-<vector>-backed
	subclass of MIDI_eventlist
*/
void ReaperMidiEventList::AddItem(MIDI_event_t *evt) // puts the item in the correct place
{
  events.push_back(evt);
}

MIDI_event_t *ReaperMidiEventList::EnumItems(int *bpos)
{
  MIDI_event_t *evt = events[(*bpos)++];

  if(*bpos >= (int) events.size())
  {
	  *bpos = 0;
  }

  return evt;
}

void ReaperMidiEventList::DeleteItem(int bpos)
{
	events.erase( events.begin() + bpos );
}

int ReaperMidiEventList::GetSize() // size of block in bytes
{
	return events.size() * sizeof(MIDI_event_t);	// chewbacca check:  does this make sense?
}

void ReaperMidiEventList::Empty()
{
  events.clear();
}

class Bridgemidi_Input : public Bridger
{
public:

	Bridgemidi_Input(lua_State *L) : Bridger(L) {}

// f-unctions
//	  virtual void start()=0;
//    virtual void stop()=0;
// takes luna{self} returns void

#define mkselftovoid(FNAME)										\
	int f##FNAME ( lua_State *L ) {								\
		midi_Input *mip = (midi_Input *) toReaperObject(L, 1);	\
		mip-> FNAME ();											\
		return 0; }

	mkselftovoid(start);
	mkselftovoid(stop);

//	  virtual void SwapBufs(unsigned int timestamp)=0;
// takes ( {self}, uint ) returns void

	int fSwapBufs(lua_State *L)
	{
		midi_Input *mip = (midi_Input *) toReaperObject(L, 1);
		unsigned int timestamp = luaL_checkinteger(L, 2);
		mip->SwapBufs(timestamp);
		return 0;
	}

//  virtual void RunPreNoteTracking(int isAccum) { }

	int fRunPreNoteTracking(lua_State *L)
	{
		midi_Input *mip = (midi_Input *) toReaperObject(L, 1);
		int isAccum = luaL_checkinteger(L, 2);
		mip->RunPreNoteTracking(isAccum);
		return 0;
	}

	//  virtual MIDI_eventlist *GetReadBuf()=0;
	// takes luna{self} returns luna{MIDI_eventlist}

	int fGetReadBuf(lua_State *L)
	{
		midi_Input *mip = (midi_Input *) toReaperObject(L, 1);
		MIDI_eventlist *melp = mip->GetReadBuf();
		pushMIDI_eventlist(L, melp);
		return 1;
	}

// luna wrapper interface:
public:
	static const char className[];
    static const Luna<Bridgemidi_Input>::RegType Register[];
};


#define bmirentry(NAME)	\
{ #NAME , &Bridgemidi_Input::f##NAME }

const char Bridgemidi_Input::className[] = "Reapermidi_Input";
const Luna<Bridgemidi_Input>::RegType Bridgemidi_Input::Register[] = {
	bmirentry(start),
	bmirentry(stop),
	bmirentry(SwapBufs),
	bmirentry(RunPreNoteTracking),
	bmirentry(GetReadBuf),

	{NULL, NULL}
};

class Bridgemidi_Output : public Bridger
{
public:

	Bridgemidi_Output(lua_State *L) : Bridger(L) {}

	// f-unctions

//  virtual void BeginBlock() { }  // outputs can implement these if they wish to have timed block sends
// takes {self} returns void
	int fBeginBlock(lua_State *L)
	{
		midi_Output *mop = (midi_Output *) toReaperObject(L, 1);
		mop->BeginBlock();
		return 0;
	}

//  virtual void EndBlock(int length, double srate, double curtempo) { }
// takes ( {self}, int length, double srate, double curtempo ) returns void
	int fEndBlock(lua_State *L)
	{
		midi_Output *mop = (midi_Output *) toReaperObject(L, 1);
		int length = luaL_checkinteger(L, 2);
		double srate = luaL_checknumber(L, 3);
		double curtempo = luaL_checknumber(L, 4);

		mop->EndBlock(length, srate, curtempo);
		return 0;
	}

//  virtual void SendMsg(MIDI_event_t *msg, int frame_offset)=0; // frame_offset can be <0 for "instant" if supported
// takes ( {self}, {BridgeMIDI_event_t} msg, int frame_offset) returns void
	int fSendMsg(lua_State *L)
	{
		midi_Output *mop = (midi_Output *) toReaperObject(L, 1);
		MIDI_event_t *msg = (MIDI_event_t *) toReaperObject(L, 2);//toMIDI_event_t(L, 2);
		int frame_offset = luaL_checkinteger(L, 3);

		mop->SendMsg(msg, frame_offset);
		return 0;
	}

//  virtual void Send(unsigned char status, unsigned char d1, unsigned char d2, int frame_offset)=0; // frame_offset can be <0 for "instant" if supported
// takes ( {self}, uchar status, uchar d1, uchar d2, int frame_offset )
// returns void
	int fSend(lua_State *L)
	{
		midi_Output *mop = (midi_Output *) toReaperObject(L, 1);
		int status = luaL_checkinteger(L, 2);
		int d1 = luaL_checkinteger(L, 3);
		int d2 = luaL_checkinteger(L, 4);
		int frame_offset = luaL_checkinteger(L, 5);

		mop->Send(status, d1, d2, frame_offset);
		return 0;
	}

	// luna wrapper interface:
public:
	static const char className[];
    static const Luna<Bridgemidi_Output>::RegType Register[];
};

#define bmorentry(NAME)	\
{ #NAME , &Bridgemidi_Output::f##NAME }

const char Bridgemidi_Output::className[] = "Reapermidi_Output";
const Luna<Bridgemidi_Output>::RegType Bridgemidi_Output::Register[] = {
	bmorentry(BeginBlock),
	bmorentry(EndBlock),
	bmorentry(SendMsg),
	bmorentry(Send),

	{NULL, NULL}
};
